/**

This file is part of MaCI/GIMnet.

MaCI/GIMnet is free software: you can redistribute it and/or modify it 
under the terms of the GNU Lesser General Public License as published 
by the Free Software Foundation, either version 3 of the License, or 
(at your option) any later version.

MaCI/GIMnet is distributed in the hope that it will be useful, but WITHOUT 
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public 
License for more details.

You should have received a copy of the GNU Lesser General Public 
License along with GIMnet. (See COPYING.LESSER) If not, see 
<http://www.gnu.org/licenses/>.

**/
/**
 * \file
 * \brief Implementation of Energy interface Server.
 * \author Antti Maula <antti.maula@tkk.fi>
 * \author Marek Matusiak
 *
 * $Id: EnergyServer.cpp,v 1.3 2009-05-13 07:18:16 amaula Exp $
 *
 */
#include "EnergyServer.hpp"
#include "EnergyData.hpp"

//*****************************************************************************
#define GIMI_CLIENT_API (20000)
#include "gimi.h"
//*****************************************************************************
//*****************************************************************************
using namespace MaCI::Energy;
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************

CEnergyServer::CEnergyServer(MaCICtrl::CMaCICtrlServer *aMaCICtrlServerPtr, 
                           const int aInterfaceMinor,
                           const std::string &aDescription,
                           const std::string &aInstanceName)
  : CMaCI(aMaCICtrlServerPtr, GIMI_PROTOCOL_MACI_ENERGY, aInterfaceMinor, aInstanceName),
    //    CSync(1,1),
    //    CThread(1),
    iServerActive(false),
    iServiceDescription(aDescription),
    iEnergySequenceNumber(0)
{
}
//*****************************************************************************

CEnergyServer::~CEnergyServer()
{
  DoClose();
}
//*****************************************************************************

MaCI::EMaCIError CEnergyServer::DoOpen(void)
{
  EMaCIError result = KMaCIError;
  assert(this != NULL);
  assert(iGIMIPtr != NULL);
  
  // Execute only if server is not active.
  if (!iServerActive) {
    // Register service.
    RegisterProvidedService("MaCI-EnergyServer; '" + iServiceDescription + "'");
    RegisterAcceptedService("MaCI-EnergyServer-Cmd; '" + iServiceDescription + "'");
    
    // Start the handler Thread
    iServerActive = true;
    //    CThread::RunThread();
    
    result = KMaCIOK;
    
    dPrint(4,"Succesfully Open():ed EnergyServer instance %p.", this);
  }
    
  return result;
}
//*****************************************************************************

MaCI::EMaCIError CEnergyServer::DoClose(void)
{
  EMaCIError result = KMaCIError;
  assert(this != NULL);
  assert(iGIMIPtr != NULL);

  // Execute block only if server is currently active.
  if (iServerActive) {
    // Stop!
    iServerActive = false;

    // Stop the handler Thread
    //    /* CThread::CancelThread(); //Removed by script */
    //    CThread::WaitThread();    

    // Unregister services
    UnregisterProvidedService();
    UnregisterAcceptedService();
    
    dPrint(4,"Succesfully Close():ed EnergyServer instance %p", this);
  }
  
  return result;
}
//*****************************************************************************

MaCI::EMaCIError CEnergyServer::SendEnergyEvent(const CEnergyData &aEnergyData)
{
  EMaCIError result = KMaCIError;
  
  if (iServerActive) {
    if (aEnergyData.GetEnergyEvent()) {
      // Update sequence.
      ++iEnergySequenceNumber;
      
      // Got
      SendInterfaceSubscriberMessage(aEnergyData.GetBinBagContainerPtr()->GetBuffer(),
                                     aEnergyData.GetBinBagContainerPtr()->GetBufferSize(),
                                     iEnergySequenceNumber);

      dPrint(10,"SendInterfaceSubscriberMessage called: %d:%d, seq: %d (%d bytes of payload sent)",
             iInterfaceMajor, iInterfaceMinor, 
             iEnergySequenceNumber-1,
             aEnergyData.GetBinBagContainerPtr()->GetBufferSize());
      // Result
      result = KMaCIOK;
      
    } else {
      dPrint(2,"No 'EnergyEvent' element in provided CEnergyData object. Will not send!");
      result = KMaCIInvalidFormat;

    }
   
  } else {
    dPrint(2,"Unable to send EnergyData - Interface is not active! (Open() not called?)");
    result = KMaCINotActive;

  }

  return result;
}
//*****************************************************************************

MaCI::EMaCIError CEnergyServer::SendReply(const CEnergyData &aData, 
                                         const SMessageSource &aSource)
{
  EMaCIError result = KMaCIError;

  // Check that server is active.
  if (iServerActive) {

    // Check that compulsory element is found. (This is ofcourse interface specific)
    if (aData.GetEnergyEvent()) {

      // Send
      result = SendSynchronousMessageReply(aData.GetBinBagContainerPtr()->GetBuffer(),
                                           aData.GetBinBagContainerPtr()->GetBufferSize(),
                                           aSource);
    
    } else {
      dPrint(2,"No 'EnergyEvent' element in provided CEnergyData object. Will not send!");
      result = KMaCIInvalidFormat;
      
    }
    
  } else {
    dPrint(2,"Unable to send EnergyData - Interface is not active! (Open() not called?)");
    result = KMaCINotActive;
    
  }

  return result;
}
//*****************************************************************************

MaCI::EMaCIError CEnergyServer::WaitCommand(CEnergyData &aData, 
                                           SMessageSource &aSource,
                                           const unsigned int aTimeout_ms)
{
  // Call ReceiveInterfaceMessage
  EMaCIError e = ReceiveInterfaceMessage(aData, aSource, aTimeout_ms);

  // Check result
  if (e == KMaCIOK) {
    // If OK is returned, a message was received and succesfully
    // converted to type CEnergyData. Basicly we don't need to do
    // anything here, but some interface may want to do some checks:
    // for example, the received message contains some specific
    // element or information before it is accepted.
  }

  return e;
}
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************

/**
 * ProcessMessage is left as example for server implementations which
 * need to process incoming commands without module (== user app)
 * intervention. For example, this kind of approach is used in
 * RangingServer, which contains function for setting some ranging
 * device properties. These properties can be queried by the client
 * without user intervention, as the processMessage processes the
 * query and replies immediately. As this behaviour is not required in
 * all server implementations, this method is disabled per default.
 *
 */
#ifdef _MACI_INTERFACE_SERVER_USE_THREAD_RECEIVER_
int CEnergyServer::ProcessMessage(const CEnergyData &aMessage, const SMessageSource &aSource)
{
  int result = 0;
  gim::binbag::CBinBag bb;
  CEnergyData id;
  bool r;

  // If this function gets called, the data element is valid EnergyData
  // and the calling client has the permission to access this
  // interface.
  CEnergyData ddr;
  ddr.CreateInternalBinBag();
  bool send_reply = false;

  const TCommand *cmd = aMessage.GetCommand();
  if (cmd) {
    dPrint(8,"Got command: '%d'", cmd->cmd);
    switch(cmd->cmd) {
      
    case KCommandDoEnergyThings:
    case KCommandDoOtherEnergyThings: {
      // Pass the DoEnergyThings and DoOtherEnergyThings to local queue.
      Lock();
      iCommandQueue.push_back(ddr);
      Notify();
      Unlock();
      break;
    }
    
    case KCommandGetRandomNumber: {
      // Process the GetRandomNumber on the thread.  As this doesn't
      // mess with class contents, locking is not required.
      
      // Got Command to get a Random Number.          
      dPrint(1,"Got Command KCommandGetRandomNumber from %08x. Replying.", aSource.gimnetID);
      
      // Yes it was! So send a reply
      const unsigned int rvalue = random();
      
      // Construct event data
      CEnergyData ddr;
      ddr.CreateInternalBinBag();
      ddr.SetTimestamp(Common::TTimestamp());
      ddr.SetEnergyEvent(TEnergyEvent(KEventTypeRandomNumber, rvalue));
      
      // Send as reply. Can be picked from queue as is - so no need
      // for extra identification information.
      ds->SendReply(ddr, aSource);
      
      dPrint(2,"Sent Random number event as reply with messageID %d", aSource.messageID);
      break;
    }
      
    default:
      dPrint(2,"Unsupported EnergyServer command %d received. Ignoring.",
             cmd->cmd);
      break;
    }
  }

  return result;
}
//*****************************************************************************

int CEnergyServer::ThreadFunction(const int aThreadNumber)
{
  int result = 0;
  const unsigned int hard_error_count_limit = 10;
  unsigned int hard_error_count = 0;
  EMaCIError e;
  CEnergyData dd;
  SMessageSource ms;

  assert(aThreadNumber == 0);
  assert(iGIMIPtr != NULL);
  
  dPrint(8, "GIMIMessage handler started");
  
  // Run as long as the thread should be active
  while(IsActive()) {
    dd.Reset();
    
    // Call the ReceiveMessage wrapper with 1s timeout. This template
    // function model is able to convert to correct type
    // automatically.
    e = ReceiveInterfaceMessage(dd, ms, 2000);
    if (e == KMaCIOK) {
      ProcessMessage(dd, ms);
      
      // Reset error counter.
      if (hard_error_count) hard_error_count = 0;
      
    } else if (e != KMaCITimeout) {
      // Increment hard error count
      ++hard_error_count;
      
      // Check whether the hard error count limit is still under limit. 
      if (hard_error_count < hard_error_count_limit) {
        // Other error, try to recover by sleeping one second.
        dPrint(10,"ProcessMessage returned error %u, trying to recover by sleeping 1000ms (%u attempts left)",
               e, hard_error_count_limit-hard_error_count);
        ownSleep_ms(1000);

      } else {
        dPrint(3,"Error Recovery failed, EnergyServer ThreadFunction terminating...");

        // Break the while().
        break;
      }
      
    }
  }

  dPrint(8, "GIMIMessage handler stopped");
  SetActive(false);

  return result;
}
#endif // _MACI_INTERFACE_SERVER_USE_THREAD_RECEIVER_
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************

